
export function expect(any) {

  return {
    toBe: function (_any) {
      if (_any !== any) {
        throw new Error(`${any} !== ${_any}`)
      }
    },
    toHaveBeenCalledTimes: function (times) {
      if (any.calls !== times) {
        throw new Error(`${any.calls}!== ${times}`)
      }
    },
    toBeUndefined: function () {
      if (any !== undefined) {
        throw new Error(`${any}!== undefined`)
      }
    },
    toBeCalledTimes: function (times) {
      if (any.calls !== times) {
        throw new Error(`${any.calls}!== ${times}`)
      }
    },
    toBeInstanceOf: function (instance) {
      if (!(any instanceof instance)) {
        throw new Error(`${any}!== ${instance}`)
      }
    },
    toMatchObject: function (obj) {
      for (const key in obj) {
        if (obj[key] !== any[key]) {
          throw new Error(`${obj[key]}!== ${any[key]}`)
        }
      }
    },
    toEqual: function (obj) {
      if (JSON.stringify(obj) !== JSON.stringify(any)) {
        throw new Error(`${JSON.stringify(obj)}!== ${JSON.stringify(any)}`)
      }
    },
    not: {
      toBe: function (_any) {
        if (_any === any) {
          throw new Error(`${any} === ${_any}`)
        }
      },
    }
  }
}

export const vi = {
  fn: (func?: (..._args: any[]) => any) => {
    const f = ((...args) => {
      const result = func?.(...args);
      f.calls++;
      return result;
    }) as MockFunction;
    f.calls = 0;
    return f;
  }
}

interface MockFunction {
  (): void;
  calls: number;
}

export function nextTick() {
  return new Promise(resolve => setTimeout(resolve, 0)) 
}

export const gc = () => {
  return new Promise<void>(resolve => {
    setTimeout(() => {
      globalThis.gc!()
      resolve()
    })
  })
}